#include "bsp_lcd.h"
#include "spi.h"
#include "usart.h"
#include "bsp_sram.h"
#include "fsmc.h"
#include "tim.h"
#include "bsp_font.h"
#include "bsp_dec_font.h"
#include "rtc.h"

static void _lcd_set_reg(uint8_t regval);
static void _lcd_wr_data(uint8_t data);
static void _lcd_setdisplay_dir(uint8_t dir);
static void _lcd_writegram_prepare(void);
static void _lcd_setdisplay_dir(uint8_t dir);
static void _lcd_ic_init(void);
static void _lcd_setcuror(uint16_t u16XPos, uint16_t u16YPos);
static void _lcd_drawpoint(uint16_t u16XPos, uint16_t u16YPos, uint16_t u16Color);
static void _lcd_setwindow(uint16_t u16StartX, 
                    uint16_t u16StartY, 
                    uint16_t u16Width, 
                    uint16_t u16Height);



//设置寄存器地址
static void _lcd_set_reg(uint8_t regval) 
{
    LCD_RS = 0;
    LCD_CS = 0;
    HAL_SPI_Transmit(&hspi1, &regval, 1, 0xFF);
    LCD_CS = 1;
}

//写LCD数据
static void _lcd_wr_data(uint8_t data)
{
    LCD_RS = 1;
    LCD_CS = 0;
    HAL_SPI_Transmit(&hspi1, &data, 1, 0xFF);
    LCD_CS = 1;
}

//LCD写GRAM
static void _lcd_writegram(uint16_t rgbdata)
{
    uint8_t rgb_h = (rgbdata >> 8);
    uint8_t rgb_l = (rgbdata & 0x00FF);
    LCD_RS = 1;
    LCD_CS = 0;
    HAL_SPI_Transmit(&hspi1, &rgb_h, 1, 0xFF);
    HAL_SPI_Transmit(&hspi1, &rgb_l, 1, 0xFF);
    LCD_CS = 1;
}


//指定寄存器地址写数据
static void _lcd_writereg(uint8_t regval, uint8_t data)
{
    _lcd_set_reg(regval);
    _lcd_wr_data(data);
}


//准备写Gram
static void _lcd_writegram_prepare(void)
{
    _lcd_set_reg(LCD_WR_CMD);
}


//设置显示方向 0：竖屏 1：横屏
static void _lcd_setdisplay_dir(uint8_t dir)
{
    uint16_t u16LcdWidth = 0;
    uint16_t u16LcdHeight = 0;
    uint8_t u8Dirreg = 0;
    uint8_t u8Regval = 0;
    if(0 == dir)    //7
    {
        u8Regval  |= (0<<7)|(1<<6)|(0<<5);
        u16LcdWidth = LCD_WIDTH;
        u16LcdHeight = LCD_HEIGHT;
	}
    else    //1
    {
        u8Regval  |= (1<<7)|(1<<6)|(1<<5);
        u16LcdWidth = LCD_HEIGHT;
        u16LcdHeight = LCD_WIDTH;
    }
    u8Dirreg  = LCD_DIR_REG; 
    u8Regval |= COLORORDER;
    _lcd_writereg(u8Dirreg, u8Regval);

    _lcd_set_reg(LCD_SETX_CMD);
    _lcd_wr_data(0);
    _lcd_wr_data(0);
    _lcd_wr_data((u16LcdWidth-1)>>8);
    _lcd_wr_data((u16LcdWidth-1)&0x00FF);
    _lcd_set_reg(LCD_SETY_CMD); 
    _lcd_wr_data(0);
    _lcd_wr_data(0);
    _lcd_wr_data((u16LcdHeight-1)>>8);
    _lcd_wr_data((u16LcdHeight-1)&0x00FF);  
}

//设置显示窗口
static void _lcd_setwindow(uint16_t u16StartX, 
                            uint16_t u16StartY, 
                            uint16_t u16Width, 
                            uint16_t u16Height)
{
    u16Width = u16StartX + u16Width - 1;
    u16Height = u16StartY + u16Height - 1;

    _lcd_set_reg(LCD_SETX_CMD);
    _lcd_wr_data(u16StartX >> 8);
    _lcd_wr_data(u16StartX & 0X00FF);
    _lcd_wr_data(u16Width >> 8);
    _lcd_wr_data(u16Width & 0X00FF);

    _lcd_set_reg(LCD_SETY_CMD);
    _lcd_wr_data(u16StartY >> 8);
    _lcd_wr_data(u16StartY & 0X00FF);
    _lcd_wr_data(u16Height >> 8);
    _lcd_wr_data(u16Height & 0X00FF);

}

//lcd ic芯片初始化
static void _lcd_ic_init(void)
{
    _lcd_set_reg(0x11);     //Sleep Out
    HAL_Delay(120);                // Delay 120ms
                
    _lcd_set_reg(0x36);     
    _lcd_wr_data(0x48);   

    _lcd_set_reg(0x3A);     //Interface Pixel Format
    _lcd_wr_data(0x55);   

    _lcd_set_reg(0xF0);     // Command Set Control
    _lcd_wr_data(0xC3);   

    _lcd_set_reg(0xF0);     
    _lcd_wr_data(0x96);   

    _lcd_set_reg(0xB4);     
    _lcd_wr_data(0x01);   

    _lcd_set_reg(0xB7);     
    _lcd_wr_data(0xC6);   

    _lcd_set_reg(0xB9);     
    _lcd_wr_data(0x02);   
    _lcd_wr_data(0xE0);

    _lcd_set_reg(0xC0);     
    _lcd_wr_data(0x80);   
    _lcd_wr_data(0x07);   

    _lcd_set_reg(0xC1);     
    _lcd_wr_data(0x15);   

    _lcd_set_reg(0xC2);     
    _lcd_wr_data(0xA7);   

    _lcd_set_reg(0xC5);     //VCOM Control  
    _lcd_wr_data(0x07);   

    _lcd_set_reg(0xE8);     
    _lcd_wr_data(0x40);   
    _lcd_wr_data(0x8A);   
    _lcd_wr_data(0x00);   
    _lcd_wr_data(0x00);   
    _lcd_wr_data(0x29);   
    _lcd_wr_data(0x19);   
    _lcd_wr_data(0xA5);   
    _lcd_wr_data(0x33);   

    _lcd_set_reg(0xE0);
    _lcd_wr_data(0xF0);
    _lcd_wr_data(0x04);
    _lcd_wr_data(0x0E);
    _lcd_wr_data(0x03);
    _lcd_wr_data(0x02);
    _lcd_wr_data(0x13);
    _lcd_wr_data(0x34);
    _lcd_wr_data(0x44);
    _lcd_wr_data(0x4A);
    _lcd_wr_data(0x3A);
    _lcd_wr_data(0x15);
    _lcd_wr_data(0x15);
    _lcd_wr_data(0x2F);
    _lcd_wr_data(0x34);

    _lcd_set_reg(0xE1);
    _lcd_wr_data(0xF0);
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x16);
    _lcd_wr_data(0x0C);
    _lcd_wr_data(0x09);
    _lcd_wr_data(0x05);
    _lcd_wr_data(0x34);
    _lcd_wr_data(0x43);
    _lcd_wr_data(0x4A);
    _lcd_wr_data(0x35);
    _lcd_wr_data(0x11);
    _lcd_wr_data(0x11);
    _lcd_wr_data(0x2C);
    _lcd_wr_data(0x32);

    _lcd_set_reg(0xF0);     
    _lcd_wr_data(0x3C);   

    _lcd_set_reg(0xF0);     
    _lcd_wr_data(0x69);  

    HAL_Delay(120);                // Delay 120ms
                
    _lcd_set_reg(0x21);     
    _lcd_set_reg(0x29);     // Display ON
}


//设置光标位置
static void _lcd_setcuror(uint16_t u16XPos, uint16_t u16YPos)
{
    _lcd_setwindow(u16XPos, u16YPos, u16XPos, u16YPos);
}

//画点
static void _lcd_drawpoint(uint16_t u16XPos, uint16_t u16YPos, uint16_t u16Color)
{
    _lcd_setcuror(u16XPos, u16YPos);
    _lcd_writegram_prepare();
    _lcd_writegram(u16Color);
}


//全屏填色
void bsp_lcd_clear(uint16_t u16Color)
{
    uint8_t u8ColorH = u16Color >> 8;
    uint8_t u8ColorL = u16Color & 0x00FF;
    uint32_t i = 0;
    uint32_t u32Total = LCD_WIDTH * LCD_HEIGHT;

    if(LCD_DIR)
    {
        _lcd_setwindow(0, 0, LCD_HEIGHT, LCD_WIDTH);
    }
    else
    {
        _lcd_setwindow(0, 0, LCD_WIDTH, LCD_HEIGHT);
    }
    
    _lcd_writegram_prepare();

    LCD_RS = 1;
    LCD_CS = 0;
    for(i = 0; i < u32Total; i++)
    {
        HAL_SPI_Transmit(&hspi1, &u8ColorH, 1, 0xFF);
        HAL_SPI_Transmit(&hspi1, &u8ColorL, 1, 0xFF); 
    }
    LCD_CS = 1;
}

//LCD初始化
void bsp_lcd_init(void)
{
    LCD_RST = 1;
    HAL_Delay(10);
    LCD_RST = 0;
    HAL_Delay(20);
    LCD_RST = 1;
    HAL_Delay(20);

    _lcd_ic_init();

    _lcd_setdisplay_dir(LCD_DIR);
    bsp_lcd_clear_fast(0x0000);
    LCD_ShowString(0,0,200,16,16,"LCD init success!",0xFFFF);
}

//指定区域填色
void bsp_lcd_fill(  uint16_t u16StartX,
                    uint16_t u16StartY,
                    uint16_t u16EndX,
                    uint16_t u16EndY,
                    uint16_t u16Color)
{
    uint16_t i,j;
    uint16_t u16Len = 0;
    u16Len = u16EndX - u16StartX + 1;
    for(i = u16StartY; i <= u16EndY; i++)
    {
        _lcd_setcuror(u16StartX, i);
        _lcd_writegram_prepare();
        for(j = 0; j < u16Len; j++)
        {
            _lcd_writegram(u16Color);
        }
    }
}

//指定区域填充指定颜色块
void bsp_lcd_fill_block(uint16_t u16StartX,
                        uint16_t u16StartY, 
                        uint16_t u16EndX,
                        uint16_t u16EndY,
                        uint16_t *u16Color)
{
    uint16_t u16Height = u16EndY - u16StartY + 1;
    uint16_t u16Width = u16EndX - u16StartX + 1;
    uint16_t i,j;
 	for(i=0;i<u16Height;i++)
	{
 		_lcd_setcuror(u16StartX,u16StartY+i);   	//设置光标位置 
		_lcd_writegram_prepare();     //开始写入GRAM
		for(j=0;j<u16Width;j++)
        {   
            _lcd_writegram(u16Color[i*u16Height+j]);//写入数据 
        }
	}
}
//显示一个字符
void LCD_ShowChar(  uint16_t u16XPos, 
                    uint16_t u16YPos, 
                    int8_t i8Chr, 
                    uint8_t u8Size, 
                    uint8_t u8Mode, 
                    uint16_t u16Color)
{  							  
    uint8_t temp, t1, t;
    uint16_t y0 = u16YPos;
    uint8_t csize = 0;
    uint8_t *pfont = 0;

    csize = (u8Size / 8 + ((u8Size % 8) ? 1 : 0)) * (u8Size / 2); /* 得到字体一个字符对应点阵集所占的字节数 */
    i8Chr = i8Chr - ' ';    /* 得到偏移后的值（ASCII字库是从空格开始取模，所以-' '就是对应字符的字库） */

    switch (u8Size)
    {
        case 12:
            pfont = (uint8_t *)asc2_1206[i8Chr];  /* 调用1206字体 */
            break;

        case 16:
            pfont = (uint8_t *)asc2_1608[i8Chr];  /* 调用1608字体 */
            break;

        default:
            return ;
    }

    for (t = 0; t < csize; t++)
    {
        temp = pfont[t];    /* 获取字符的点阵数据 */

        for (t1 = 0; t1 < 8; t1++)   /* 一个字节8个点 */
        {
            if (temp & 0x80)        /* 有效点,需要显示 */
            {
                _lcd_drawpoint(u16XPos, u16YPos, u16Color);        /* 画点出来,要显示这个点 */
            }
            else if (u8Mode == 0)     /* 无效点,不显示 */
            {
                _lcd_drawpoint(u16XPos, u16YPos, 0x0000); /* 画背景色,相当于这个点不显示(注意背景色由全局变量控制) */
            }

            temp <<= 1; /* 移位, 以便获取下一个位的状态 */
            u16YPos++;

            if (u16YPos >= LCD_WIDTH)return;  /* 超区域了 */

            if ((u16YPos - y0) == u8Size)   /* 显示完一列了? */
            {
                u16YPos = y0; /* y坐标复位 */
                u16XPos++;    /* x坐标递增 */

                if (u16XPos >= LCD_HEIGHT)return;   /* x坐标超区域了 */

                break;
            }
        }
    }
}   

//显示字符串
void LCD_ShowString(uint16_t x,uint16_t y,uint16_t width,uint16_t height,uint8_t size,uint8_t *p,uint16_t color)
{         
	uint8_t x0=x;
	width+=x;
	height+=y;
    while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
    {       
        if(x>=width){x=x0;y+=size;}
        if(y>=height)break;//退出
        LCD_ShowChar(x,y,*p,size,0,color);
        x+=size/2;
        p++;
    }  
}


//指定区域填充指定颜色块-快速版
void bsp_lcd_fill_block_fast(   uint16_t u16StartX,
                                uint16_t u16StartY, 
                                uint16_t u16EndX,
                                uint16_t u16EndY,
                                uint16_t *u16Color)
{
    uint8_t *pu8ColorBuf = (uint8_t *)u16Color;
    uint16_t u16Height = u16EndY - u16StartY + 1;
    uint16_t u16Width = u16EndX - u16StartX + 1;
    uint16_t i,j;

    if(1600 >= u16Height * u16Width)  //当颜色块小于于40*40个像素时，使用SPI传输效率高
    {
        for(i=0;i<u16Height;i++)
        {
            _lcd_setcuror(u16StartX,u16StartY+i);   	//设置光标位置 
            _lcd_writegram_prepare();     //开始写入GRAM
            for(j=0;j<u16Width;j++)
            {   
                _lcd_writegram(u16Color[i*u16Height+j]);//写入数据 
            }
        }
    }
    else
    {
        for(i=0;i<u16Height;i++)
        {
            _lcd_setcuror(u16StartX,u16StartY+i);   	//设置光标位置 
            _lcd_writegram_prepare();     //开始写入GRAM
            LCD_RS = 1;
            LCD_CS = 0;
            HAL_SPI_Transmit_DMA(&hspi1, pu8ColorBuf, u16Width*2);
            delay_us(400);                                                  //经实验得出，当延迟为400us时，DMA传输出错概率最低，速度最快
            HAL_SPI_Abort(&hspi1);
            LCD_CS = 1;
            pu8ColorBuf += (u16Width*2);
        }
    }
}



//全屏填充指定颜色块-快速版
void bsp_lcd_clear_fast(uint16_t u16Color)
{	
    uint32_t i;
    uint16_t *pu16ColorBuf = (uint16_t *)LCD_MEM_START_ADDR;

    for(i = 0; i < DMA_PER_SIZE; i++)
    {
        pu16ColorBuf[i] = u16Color;
    }

    if(LCD_DIR)
    {
        _lcd_setwindow(0, 0, LCD_HEIGHT, LCD_WIDTH);
    }
    else
    {
        _lcd_setwindow(0, 0, LCD_WIDTH, LCD_HEIGHT);
    }
    
    _lcd_writegram_prepare();

    LCD_RS = 1;
    LCD_CS = 0;
    
    
    for(i = 0; i < DMA_LOOP_NUM; i++)
    {
        HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)pu16ColorBuf, DMA_PER_SIZE);
        HAL_Delay(2);
        HAL_SPI_Abort(&hspi1);
    }
    
    if(DMA_LOOP_NUM_LEFT)
    {
        HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)pu16ColorBuf, DMA_LOOP_NUM_LEFT);
        HAL_Delay(1);
        HAL_SPI_Abort(&hspi1);
    }
    

    LCD_CS = 1;	
}


//显示中文
void bsp_lcd_showChinese(   uint16_t u16XPos,
                            uint16_t u16YPos,
                            uint16_t u16gb2312code,
                            uint16_t u16Color,
                            uint16_t u16BgColor)
{
    uint8_t *pu8FontAddr = (uint8_t *)bsp_get_gb2312_sramaddr(u16gb2312code);
    uint16_t u16y0 = u16YPos;
    uint8_t i = 0;

    uint8_t u8Temp;
    
    for (i = 0; i < 32; i++)
    {
        u8Temp = pu8FontAddr[i];    /* 获取字符的点阵数据 */

        for (uint8_t j = 0; j < 8; j++)   /* 一个字节8个点 */
        {
            if (u8Temp & 0x80)        /* 有效点,需要显示 */
            {
                _lcd_drawpoint(u16YPos, u16XPos, u16Color);        /* 画点出来,要显示这个点 */
            }
            else     
            {
                _lcd_drawpoint(u16YPos, u16XPos, u16BgColor); /* 画背景色,相当于这个点不显示(注意背景色由全局变量控制) */
            }

            u8Temp <<= 1; /* 移位, 以便获取下一个位的状态 */
            u16YPos++;

            if (u16YPos >= LCD_HEIGHT)return;  /* 超区域了 */

            if ((u16YPos - u16y0) == 16)   /* 显示完一列了? */
            {
                u16YPos = u16y0; /* y坐标复位 */
                u16XPos++;    /* x坐标递增 */
                if (u16XPos >= LCD_WIDTH)return;   /* x坐标超区域了 */
                break;
            }
        }
    }


}


//显示中文字符串
void bsp_lcd_showChineseStr(    uint16_t u16XPos,
                                uint16_t u16YPos,
                                uint16_t u16Color,
                                uint16_t u16BgColor)
{
    uint16_t u16StrLen = *((uint16_t *)(FONT_SRC_MEM_LEN_START_ADDR));
    uint16_t u16FontData = 0;
    uint8_t *pu8FontPtr = (uint8_t *)(FONT_SRC_MEM_START_ADDR);
    uint8_t u8ChineseFontCnt = 0;
    uint8_t u8AsciiFontCnt = 0;
    uint16_t u16FontPos = 0;
    for(uint16_t i = 0; i < u16StrLen; i++)
    {
        u16FontPos = u16YPos + u8ChineseFontCnt * 16 + u8AsciiFontCnt * 8;
        if(*pu8FontPtr > 0x7A)  //如果大于0x7A,则说明是汉字
        {
            u16FontData = *((uint16_t *)(pu8FontPtr));
            bsp_lcd_showChinese(u16XPos, u16FontPos,u16FontData,u16Color,u16BgColor);
            pu8FontPtr  += 2;
            u8ChineseFontCnt ++;
        }
        else
        {
            LCD_ShowChar(u16FontPos,u16XPos,*pu8FontPtr,16,1,u16Color);
            pu8FontPtr  += 1;
            u8AsciiFontCnt ++;
        }
    }
}


void bsp_lcd_showTime(          uint16_t u16XPos,
                                uint16_t u16YPos,
                                uint16_t u16Color)
{
    char cStr[20] = {0};
    HAL_RTC_GetTime(&hrtc, &g_rtcTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &g_rtcDate, RTC_FORMAT_BIN);
    sprintf(cStr,"20%02d-%02d-%02d %02d:%02d:%02d",g_rtcDate.Year,g_rtcDate.Month,g_rtcDate.Date,g_rtcTime.Hours,g_rtcTime.Minutes,g_rtcTime.Seconds);
    LCD_ShowString(u16XPos, u16YPos, 320,16,16,(uint8_t *)cStr,0xFFFF);
}
